package com.codemonkey.lq.extend.springmvc;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.codemonkey.lq.common.builder.Builders;
import com.codemonkey.lq.common.util.StringHelper;

/**
 * From 
 *    http://www.cnblogs.com/liaojie970/p/5602780.html <br>
 *    
 * File Upload Download
 * 
 * @Description 操作文件上传,下载
 * @author LJ
 * @Date 2016年6月8日 上午11:42:44
 * @Version v1.0
 */
public final class FileUploadDownloader {
	private static final String ENCODING = "utf-8";

	/**
	 * 文件下载
	 * 
	 * @param filePath
	 *            文件路径
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 */
	public static ResponseEntity<byte[]> download(String filePath)
			throws UnsupportedEncodingException, IOException {
		String fileName = FilenameUtils.getName(filePath);
		return downloadAssist(filePath, fileName);
	}

	/**
	 * 文件下载
	 * 
	 * @param filePath
	 *            文件路径
	 * @param fileName
	 *            浏览器端保存的文件名
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 */
	public static ResponseEntity<byte[]> download(String filePath, String fileName)
			throws UnsupportedEncodingException, IOException {
		return downloadAssist(filePath, fileName);
	}

	/**
	 * 文件下载辅助
	 * 
	 * @param filePath
	 *            文件路径
	 * @param fileName
	 *            文件名
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 */
	private static ResponseEntity<byte[]> downloadAssist(String filePath, String fileName)
			throws UnsupportedEncodingException, IOException {
		File file = new File(filePath);
		if (!file.isFile() || !file.exists()) {
			throw new IllegalArgumentException("filePath 参数必须是真实存在的文件路径:" + filePath);
		}
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
		headers.setContentDispositionFormData("attachment", URLEncoder.encode(fileName, ENCODING));
		return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers,
				HttpStatus.CREATED);
	}

	/**
	 * 页面展示
	 * @param is
	 * @param response
	 */
	public static void download(InputStream is, final String fileNameInClient,
			HttpServletResponse response) {
		assert null != is;
		if (StringHelper.isNotEmpty(fileNameInClient)) {
			try {
				// 会导致下载操作,而非页面打开
				response.addHeader("Content-Disposition", "attachment;fileName="
						+ URLEncoder.encode(fileNameInClient, ENCODING));// 设置文件名
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		byte[] buffer = new byte[1024];
		BufferedInputStream bis = null;
		try {
			bis = new BufferedInputStream(is);
			OutputStream os = response.getOutputStream();
			int i = bis.read(buffer);
			while (i != -1) {
				os.write(buffer, 0, i);
				i = bis.read(buffer);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(bis);
		}
	}

	/*---------------------------------------- 上传文件操作---------------------------------------------*/

	/**
	 * 多文件上传(当然包含单个文件上传这种形式!)
	 * 
	 * @param request
	 *            当前上传的请求
	 * @param basePath
	 *            保存文件的路径
	 * @throws IOException
	 * @throws IllegalStateException
	 * @return Map<String, String> 返回上传文件的保存路径 以文件名做map的key;文件保存路径作为map的value
	 */
	public static void multiFileUpload(HttpServletRequest request, String basePath)
			throws IllegalStateException, IOException {
		if (!(new File(basePath).isDirectory())) {
			throw new IllegalArgumentException("basePath 参数必须是文件夹路径");
		}
		multiFileUpload(request, basePath, null);
	}

	/**
	 * 多文件上传(当然包含单个文件上传这种形式!)
	 * 
	 * @param request
	 *            当前上传的请求
	 * @param basePath
	 *            保存文件的路径
	 * @param exclude
	 *            排除文件名字符串,以逗号分隔的,默认无可传null
	 * @throws IllegalStateException
	 * @throws IOException
	 */
	public static void multiFileUpload(HttpServletRequest request, final String basePath,
			final String exclude) throws IllegalStateException, IOException {
		if (!(new File(basePath).isDirectory())) {
			throw new IllegalArgumentException("basePath 参数必须是文件夹路径");
		}

		multifileUploadAssist(request, new AbstractFileUploadInterceptor() {
			@Override
			public void doUpload(Map<String, Object> context) {
				final String fileFinalName = context.get(
						FileUploadInterceptor.CONTEXT_KEY_FILENAME_FINAL).toString();
				final MultipartFile multipartFile = (MultipartFile) context
						.get(FileUploadInterceptor.CONTEXT_KEY_MULTIPARTFILE_OBJECT);
				final File file = new File(basePath + fileFinalName);
				try {
					multipartFile.transferTo(file);
				} catch (IllegalStateException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			@Override
			public boolean preUpload(Map<String, Object> context) {
				if (StringHelper.isEmpty(exclude)) {
					return true;
				}

				final String fileOriginName = context.get(
						FileUploadInterceptor.CONTEXT_KEY_FILENAME_ORIGIN).toString();
				return (!exclude.contains(fileOriginName));
			}

		});
	}

	/**
	 * 多文件上传辅助, 这是灵活性最高的方法! <b>interceptor给予了本方法无与伦比的灵活性.</b>
	 * 
	 * @param request
	 *            当前上传的请求
	 * @param interceptor
	 * 			  拦截性接口, 允许调用者参与生命周期.可为null
	 * @throws IOException
	 */
	public static void multifileUploadAssist(HttpServletRequest request,
			FileUploadInterceptor interceptor) throws IOException {
		interceptor = (null == interceptor ? EmptyFileUploadInterceptor.INSTANCE : interceptor);

		// 创建一个通用的多部分解析器
		final CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request
				.getServletContext());
		// 判断 request 是否有文件上传,即多部分请求
		if (!multipartResolver.isMultipart(request)) {
			return;
		}

		// 转换成多部分request
		MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
		// get the parameter names of the MULTIPART files contained in this
		// request
		Iterator<String> iter = multiRequest.getFileNames();
		while (iter.hasNext()) {
			// 取得上传文件
			List<MultipartFile> multipartFiles = multiRequest.getFiles(iter.next());
			for (MultipartFile multipartFile : multipartFiles) {
				String fileName = multipartFile.getOriginalFilename();
				if (StringHelper.isEmpty(fileName)) {
					continue;
				}

				final Map<String, Object> context = Builders.newMapBuilder().append(
						FileUploadInterceptor.CONTEXT_KEY_FILENAME_ORIGIN, fileName).append(
						FileUploadInterceptor.CONTEXT_KEY_MULTIPARTFILE_OBJECT, multipartFile)
						.build();

				// 是否继续接下来的操作
				boolean isContinue = interceptor.preUpload(context);
				if (!isContinue) {
					continue;
				}

				// 再给外界一个修改最终保存的文件名的机会. 默认是UUID+后缀名
				final String finalFileName = interceptor.convertFileFinalName(context);

				context.put(FileUploadInterceptor.CONTEXT_KEY_FILENAME_FINAL, finalFileName);

				interceptor.doUpload(context);
			}
		}
	}

	/**
	 * 删除文件
	 * 
	 * @param filePath
	 */
	@SuppressWarnings("unused")
	private static void deleteFileIfExist(String filePath) {
		try {
			File file = new File(filePath);
			if (file.exists() && file.isFile()) {
				file.delete();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}